//
// Created by juno on 2021/9/11.
//
#define _GNU_SOURCE
#include <stdio.h>
#include <json-c/json.h>
#include "message.h"
#include "member.h"
#include "room.h"
#include "ws_server.h"
#include "common.h"

void list_room_cb(struct room_t *room, void *arg)
{
    json_object *array = arg;
    json_object *obj = json_object_new_object();

    json_object *jso_name = json_object_new_string(room->info->name);
    json_object *jso_owner = json_object_new_string(room->info->owner->base->name);
    json_object *jso_id = json_object_new_uint64(room->info->id);

    if (!jso_name || !jso_owner || !jso_id)
    {
        if (jso_name)
            json_object_put(jso_name);
        if (jso_owner)
            json_object_put(jso_owner);
        if (jso_id)
            json_object_put(jso_id);

        json_object_put(obj);

        return;
    }

    json_object_object_add(obj, "name", jso_name);
    json_object_object_add(obj, "owner", jso_owner);
    json_object_object_add(obj, "id", jso_id);

    json_object_array_add(array, obj);
}

static int build_room_list(void **res)
{
    json_object *list = json_object_new_array();

    struct msg *msg = calloc(1, sizeof(struct msg));
    size_t len = 0;

    if (!list)
    {
        free(msg);
        return -1;
    }

    rooms_iterator(list_room_cb, list);
    len = json_object_array_length(list);

    *res = list;

    return (int)len;
}

static int brocast_message(const char *src, struct room_t *room, const char *msg)
{
    struct member_t *curr = room->member;
    struct member_t *next = NULL;

    do {
        next = curr->next;
        struct session_context ctx = {0};
        ctx.session = curr->session;
        ctx.host = curr->host;

        if (room->info->owner == curr)
            continue;

        insert_message(&ctx, src,"msg", 0, NORMAL_MSG, msg);

        curr = next;
    }while(curr);


    return 0;
}

int process_message(struct session_context *ctx, json_object *msg)
{
    int type;
    const char *name = NULL;
    const char *token = NULL;
    char *room_name = NULL;

    json_object *jso_type = json_object_object_get(msg, "type");
    json_object *jso_name = json_object_object_get(msg, "name");
    json_object *jso_token = json_object_object_get(msg, "token");
    json_object *jso_msg = json_object_object_get(msg, "msg");

    if (!jso_type)
        return -1;

    type = json_object_get_int(jso_type);

    switch(type)
    {
        case GET_TOKEN:
            {
                struct member_base_info base = {0};
                struct member_t *member = NULL;

                if (!jso_name)
                    return -1;

                name = json_object_get_string(jso_name);

                if (gen_token(&ctx->session->token) < 0)
                    return -1;

                base.name = strdup(name);
                base.uuid = ctx->session->token;

                if (member_new(&base, &member) < 0)
                    return -1;

                member->session = ctx->session;
                member->host = ctx->host;

                ctx->session->member = member;

                insert_message(ctx, NULL, "token", 0, 0, base.uuid);
            }
            break;
        case NORMAL_MSG:
            {
                struct member_t *member = ctx->session->member;
                struct room_t *room = member->room;

                if (!room)
                {
                    insert_message(ctx, "系统", "msg", 0, 1, "你没有加入任何房间");
                    return -1;
                }

                if (!jso_msg)
                    return -1;

                const char *str_msg = json_object_get_string(jso_msg);

                if (!jso_token)
                    return -1;

                token = json_object_get_string(jso_token);

                if (!strcmp(token, member->base->uuid))
                    brocast_message(member->base->name, room, str_msg);
                else
                    return -1;
            }
            break;
        case JOIN_ROOM:
            {
                struct member_t *member = ctx->session->member;
                json_object *jso_room = json_object_object_get(msg, "room_name");
                int ret = 0;

                if (!jso_token)
                    return -1;

                if (!jso_room)
                    return -1;

                token = json_object_get_string(jso_token);

                if (!strcmp(token, member->base->uuid))
                {
                    room_name = json_object_get_string(jso_room);
                    ret = member_join_room(member, room_name);
                    if (ret == 0)
                    {
                        char *str = NULL;
                        asprintf(&str, "欢迎加入%s", member->room->info->name);
                        insert_message(ctx, member->room->info->owner->base->name, "msg", 0, 5, str);
                        free(str);
                    }
                }
            }
            break;
        case GET_ROOM_LIST:
            {
                void *list = NULL;
                const char *str = NULL;
                size_t len = 0;
                int ret = 0;
                struct member_t *member = ctx->session->member;

                if (!jso_token)
                    return -1;

                token = json_object_get_string(jso_token);

                if (!strcmp(member->base->uuid, token))
                {
                    ret = build_room_list(&list);

                    if (ret < 0)
                        return -1;
                    if (ret == 0)
                        return (int)insert_message(ctx, NULL, NULL, 0, 4, NULL);

                    str = json_object_to_json_string_length(list, JSON_C_TO_STRING_PLAIN, &len);

                    insert_message(ctx, NULL, "list", 0, 3, str);

                }
            }
            break;
        case LEAVE_ROOM:
            {
                struct member_t *member = ctx->session->member;

                if (!member->room)
                    return -1;

                if (!jso_token)
                    return -1;

                token = json_object_get_string(jso_token);

                if (!strcmp(token, member->base->uuid))
                {
                    member_leave_room(member);
                }
                return 0;
            }

            break;
        case CREATE_ROOM:
            {
                struct member_t *member = ctx->session->member;

                if (!jso_token)
                    return -1;

                token = json_object_get_string(jso_token);

                if (!strcmp(member->base->uuid, token))
                {
                    json_object *jso_room = json_object_object_get(msg, "room_name");
                    if (!jso_room)
                        return -1;

                    room_name = json_object_get_string(jso_room);

                    struct room_t *room = NULL;
                    if (member_create_room(member, room_name, &room) < 0)
                    {
                        return -1;
                    }
                    member->room = room;
                }
            }
            break;
        default:
            return -1;
    }

    return 0;
}

size_t insert_message(struct session_context *ctx, const char *src, const char *name, int value, int type, const char *str)
{
    size_t len = 0;
    json_object *req = json_object_new_object();
    json_object *val = json_object_new_int(value);
    json_object *jso_type = json_object_new_int(type);
    json_object *jso_src = NULL;
    json_object *msg = NULL;
    size_t ret;
    struct msg amsg = {0};

    if (str)
        msg = json_object_new_string(str);

    json_object_object_add(req, "value", val);
    json_object_object_add(req, "type", jso_type);

    if (src)
    {
        jso_src = json_object_new_string(src);
        json_object_object_add(req, "from", jso_src);
    }

    if (str)
        json_object_object_add(req, name, msg);

    const char *msg_str = json_object_to_json_string_length(req, JSON_C_TO_STRING_PLAIN, &len);

    if (len)
    {
        memset(&amsg, 0, sizeof(struct msg));
        amsg.payload = calloc(1, LWS_PRE + len + 1);
        if (!amsg.payload)
            return -1;

        amsg.len = len;

        strncpy((char *)amsg.payload + LWS_PRE, msg_str, len);
        ret = lws_ring_insert(ctx->session->tx_ring, &amsg, 1);

        if (ret != 1)
            destroy_message(&amsg);
        else
            lws_cancel_service(ctx->host->ctx);
    }
    json_object_put(req);

    return len;
}
